package com.xing.server.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjectUtil;
import com.xing.entity.MinioEntity;
import com.xing.entity.OssFile;
import com.xing.server.OssTemplate;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.stream.Stream;

import static com.google.common.io.Files.getFileExtension;

/**
 * @version 1.0
 * @Author Xing
 * @Date 2024/4/12 15:33
 */
@Service
@Slf4j
public class MinioTemplate implements OssTemplate {

    @Autowired
    private MinioClient client;

    @Autowired
    private MinioEntity entity;

    private static final String ENCODING = "UTF-8";

    @Override
    public boolean bucketExists(String bucketName) {
        try {
            return client.bucketExists(BucketExistsArgs.builder().bucket(entity.getBucketName()).build());
        }catch (Exception e) {
            log.error("minio bucketExists Exception:{}", e);
        }
        return false;
    }

    /**
     * 创建桶
     * @param bucketName
     */
    public void makeBucket(String bucketName) {
        try {
            if (!bucketExists(bucketName)) {
                client.makeBucket(MakeBucketArgs
                        .builder()
                        .bucket(bucketName)
                        .build());
                log.info("minio makeBucket success bucketName:{}", bucketName);
            }
        } catch (Exception e) {
            log.error("minio makeBucket Exception:{}", e);
        }
    }

    @Override
    public OssFile getOssInfo(String fileName) {
        try{
            StatObjectResponse statObject = client.statObject(StatObjectArgs.builder()
                    .bucket(entity.getBucketName())
                    .object(fileName)
                    .build());
            OssFile ossFile = new OssFile();
            ossFile.setName(ObjectUtil.isEmpty(statObject.object()) ? fileName : statObject.object());
            ossFile.setFilePath(ossFile.getName());
            ossFile.setDomain(entity.getEndpoint()+ StrPool.SLASH+entity.getBucketName());
            ossFile.setHash(String.valueOf(statObject.hashCode()));
            ossFile.setSize(statObject.size());
            ossFile.setPutTime(DateUtil.date(statObject.lastModified().toLocalDateTime()));
            ossFile.setContentType(statObject.contentType());
            return ossFile;
        }catch (Exception e) {
            log.error("minio statObject Exception:{}", e);
        }
        return null;
    }

    @Override
    public OssFile upLoadFile(String folderName, String fileName, MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("400");
        }
        // 文件大小
        if (file.getSize() > 64 * 1024 * 1024) {
            throw new RuntimeException("400");
        }
        String suffix = getFileExtension(file.getOriginalFilename());
        try {
            return upLoadFile(folderName, fileName, suffix, file.getInputStream());
        } catch (Exception e) {
            log.error("minio upLoadFile Exception:{}", e);
            throw new RuntimeException("400");
        }
    }

    @Override
    public OssFile upLoadFile(String folderName, String fileName, String suffix, InputStream stream) throws IOException {
        if (!bucketExists(entity.getBucketName())) {
            log.info("minio bucketName is not creat");
            makeBucket(entity.getBucketName());
        }
        OssFile file = new OssFile();
        String originalName = fileName;
        String filePath = getFilePath(folderName, fileName, suffix);
        try {
            client.putObject(PutObjectArgs.builder().bucket(entity.getBucketName()).object(filePath)
                    .stream(stream, stream.available(), -1).contentType("application/octet" + "-stream").build());
        }catch (Exception e) {
            log.error("minio upLoadFile Exception:{}", e);
        }
        file.setOriginalName(originalName);
        file.setName(filePath);
        file.setDomain(entity.getEndpoint()+ StrPool.SLASH+entity.getBucketName());
        file.setFilePath(file.getDomain()+filePath);
        stream.close();
        log.info("minio upLoadFile success, filePath:{}", filePath);
        return file;
    }

    @Override
    public boolean removeFile(String fileName) {
        try {
            client.removeObject(
                    RemoveObjectArgs.builder().bucket(entity.getBucketName()).object(fileName)
                            .build());
            log.info("minio removeFile success, fileName:{}", fileName);
            return true;
        } catch (Exception e) {
            log.error("minio removeFile fail, fileName:{}, Exception:{}", fileName, e);
        }
        return false;
    }

    @Override
    public boolean removeFiles(List<String> fileNames) {
        try {
            Stream<DeleteObject> stream = fileNames.stream().map(DeleteObject::new);
            client.removeObjects(RemoveObjectsArgs.builder().bucket(entity.getBucketName())
                    .objects(stream::iterator).build());
            log.info("minio removeFiles success, fileNames:{}", fileNames);
            return true;
        } catch (Exception e) {
            log.error("minio removeFiles fail, fileNames:{}, Exception:{}", fileNames, e);
        }
        return false;
    }

    @Override
    public void downloadFile(HttpServletResponse response, String fileName, String filePath) {
        GetObjectResponse is = null;
        try {
            GetObjectArgs getObjectArgs =
                    GetObjectArgs.builder().bucket(entity.getBucketName()).object(filePath)
                            .build();
            is = client.getObject(getObjectArgs);
            // 设置文件ContentType类型，这样设置，会自动判断下载文件类型
            response.setContentType("application/x-msdownload");
            response.setCharacterEncoding(ENCODING);
            // 设置文件头：最后一个参数是设置下载的文件名并编码为UTF-8
            response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, ENCODING));
            IoUtil.copy(is, response.getOutputStream());
            log.info("minio downloadFile success, filePath:{}", filePath);
        } catch (Exception e) {
            log.error("minio downloadFile Exception:{}", e);
        } finally {
            IoUtil.close(is);
        }
    }
    /**
     * 获取文件外链
     *
     * @param bucketName bucket名称
     * @param fileName   文件名称
     * @param expires    过期时间
     * @return url
     */
    public String getPresignedObjectUrl(String bucketName, String fileName, Integer expires) {
        String link = "";
        try {
            link = client.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(entity.getBucketName())
                            .object(fileName).expiry(expires).build());
        } catch (Exception e) {
            log.error("minio getPresignedObjectUrl is fail, fileName:{}", fileName);
        }
        return link;
    }

    /**
     * 根据规则生成文件路径
     *
     * @param folderName       上传的文件夹名称
     * @param originalFilename 原始文件名
     * @param suffix           文件后缀名
     * @return string 上传的文件夹名称/yyyyMMdd/原始文件名_时间戳.文件后缀名
     */
    private String getFilePath(String folderName, String originalFilename, String suffix) {
        return StrPool.SLASH + String.join(StrPool.SLASH, folderName, DateUtil.date().toString("yyyy-MM-dd"),
                originalFilename) + StrPool.C_UNDERLINE + DateUtil.current() + StrPool.DOT + suffix;
    }
}
